/**
 * 
 */
package com.et114.components.ehcache;

import java.io.IOException;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.constructs.blocking.BlockingCache;
import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
import net.sf.ehcache.constructs.blocking.SelfPopulatingCache;
import net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory;
import net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache;
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

//import org.springframework.beans.factory.BeanNameAware;
//import org.springframework.beans.factory.FactoryBean;
//import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

/*
 * @author spring2.5 modify  by guanhw
 */
public class EhCacheFactoryBean /*implements FactoryBean , BeanNameAware ,InitializingBean */{
	protected final Log					logger							= LogFactory.getLog ( getClass ( ) );
	private CacheManager				cacheManager;
//	private String						cacheName;
	private int							maxElementsInMemory				= 10;
	private int							maxElementsOnDisk				= 10000000;
	private MemoryStoreEvictionPolicy	memoryStoreEvictionPolicy		= MemoryStoreEvictionPolicy.FIFO;
	private boolean						overflowToDisk					= true;
	private String						diskStorePath;
	private boolean						eternal							= false;
	private int							timeToLive						= 0;
	private int							timeToIdle						= 0;
	private boolean						diskPersistent					= false;
	private int							diskExpiryThreadIntervalSeconds	= 0;
	private boolean						blocking						= false;
	private CacheEntryFactory			cacheEntryFactory;
	private String						beanName;
	private Ehcache						cache;
	
	/**
	 * Set a CacheManager from which to retrieve a named Cache instance. By
	 * default, <code>CacheManager.getInstance()</code> will be called.
	 * <p>
	 * Note that in particular for persistent caches, it is advisable to
	 * properly handle the shutdown of the CacheManager: Set up a separate
	 * EhCacheManagerFactoryBean and pass a reference to this bean property.
	 * <p>
	 * A separate EhCacheManagerFactoryBean is also necessary for loading
	 * EHCache configuration from a non-default config location.
	 * 
	 * @see EhCacheManagerFactoryBean
	 * @see net.sf.ehcache.CacheManager#getInstance
	 */
	public void setCacheManager ( CacheManager cacheManager ) {
		this.cacheManager = cacheManager;
	}
	
	/**
	 * Set a name for which to retrieve or create a cache instance. Default is
	 * the bean name of this EhCacheFactoryBean.
	 */
//	public void setCacheName ( String cacheName ) {
//		this.cacheName = cacheName;
//	}
	
	/**
	 * Specify the maximum number of cached objects in memory. Default is 10000
	 * elements.
	 */
	public void setMaxElementsInMemory ( int maxElementsInMemory ) {
		this.maxElementsInMemory = maxElementsInMemory;
	}
	
	/**
	 * Specify the maximum number of cached objects on disk. Default is 10000000
	 * elements.
	 */
	public void setMaxElementsOnDisk ( int maxElementsOnDisk ) {
		this.maxElementsOnDisk = maxElementsOnDisk;
	}
	
	/**
	 * Set the memory style eviction policy for this cache. Supported values are
	 * "LRU", "LFU" and "FIFO", according to the constants defined in EHCache's
	 * MemoryStoreEvictionPolicy class. Default is "LRU".
	 */
	public void setMemoryStoreEvictionPolicy (
			MemoryStoreEvictionPolicy memoryStoreEvictionPolicy ) {
		Assert.notNull ( memoryStoreEvictionPolicy ,
				"memoryStoreEvictionPolicy must not be null" );
		this.memoryStoreEvictionPolicy = memoryStoreEvictionPolicy;
	}
	
	/**
	 * Set whether elements can overflow to disk when the in-memory cache has
	 * reached the maximum size limit. Default is "true".
	 */
	public void setOverflowToDisk ( boolean overflowToDisk ) {
		this.overflowToDisk = overflowToDisk;
	}
	
	/**
	 * Set whether elements are considered as eternal. If "true", timeouts are
	 * ignored and the element is never expired. Default is "false".
	 */
	public void setEternal ( boolean eternal ) {
		this.eternal = eternal;
	}
	
	/**
	 * Set t he time in seconds to live for an element before it expires, i.e.
	 * the maximum time between creation time and when an element expires. It is
	 * only used if the element is not eternal. Default is 120 seconds.
	 */
	public void setTimeToLive ( int timeToLive ) {
		this.timeToLive = timeToLive;
	}
	
	/**
	 * Set the time in seconds to idle for an element before it expires, that
	 * is, the maximum amount of time between accesses before an element
	 * expires. This is only used if the element is not eternal. Default is 120
	 * seconds.
	 */
	public void setTimeToIdle ( int timeToIdle ) {
		this.timeToIdle = timeToIdle;
	}
	
	/**
	 * Set whether the disk store persists between restarts of the Virtual
	 * Machine. The default is "false".
	 */
	public void setDiskPersistent ( boolean diskPersistent ) {
		this.diskPersistent = diskPersistent;
	}
	
	/**
	 * Set the number of seconds between runs of the disk expiry thread. The
	 * default is 120 seconds.
	 */
	public void setDiskExpiryThreadIntervalSeconds (
			int diskExpiryThreadIntervalSeconds ) {
		this.diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;
	}
	
	/**
	 * Set whether to use a blocking cache that lets read attempts block until
	 * the requested element is created.
	 * <p>
	 * If you intend to build a self-populating blocking cache, consider
	 * specifying a {@link #setCacheEntryFactory CacheEntryFactory}.
	 * 
	 * @see net.sf.ehcache.constructs.blocking.BlockingCache
	 * @see #setCacheEntryFactory
	 */
	public void setBlocking ( boolean blocking ) {
		this.blocking = blocking;
	}
	
	/**
	 * Set an EHCache
	 * {@link net.sf.ehcache.constructs.blocking.CacheEntryFactory} to use for a
	 * self-populating cache. If such a factory is specified, the cache will be
	 * decorated with EHCache's
	 * {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}.
	 * <p>
	 * The specified factory can be of type
	 * {@link net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory},
	 * which will lead to the use of an
	 * {@link net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache}.
	 * <p>
	 * Note: Any such self-populating cache is automatically a blocking cache.
	 * 
	 * @see net.sf.ehcache.constructs.blocking.SelfPopulatingCache
	 * @see net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache
	 * @see net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory
	 */
	public void setCacheEntryFactory ( CacheEntryFactory cacheEntryFactory ) {
		this.cacheEntryFactory = cacheEntryFactory;
	}
	
	public void setBeanName ( String name ) {
		this.beanName = name;
	}
	/**
	 * 原名afterPropertiesSet
	 * @throws CacheException
	 * @throws IOException
	 */
	public Ehcache createEhCache ( String cacheName ) throws CacheException , IOException {
		// If no CacheManager given, fetch the default.
		if ( this.cacheManager == null ) {
			if ( logger.isDebugEnabled ( ) ) {
				logger.debug ( "Using default EHCache CacheManager for cache region '"
								+ cacheName + "'" );
			}
			this.cacheManager = CacheManager.getInstance ( );
		}
		
		// If no cache name given, use bean name as cache name.
		if ( cacheName == null ) {
			cacheName = this.beanName;
		}
		
		Assert.notNull ( cacheName , "缓存名称不能为空！" );
		// Fetch cache region: If none with the given name exists,
		// create one on the fly.
		Ehcache rawCache = null;
		if ( this.cacheManager.cacheExists ( cacheName ) ) {
			if ( logger.isDebugEnabled ( ) ) {
				logger.debug ( "Using existing EHCache cache region '"
						+ cacheName + "'" );
			}
			rawCache = this.cacheManager.getEhcache ( cacheName );
		} else {
			if ( logger.isDebugEnabled ( ) ) {
				logger.debug ( "Creating new EHCache cache region '"
						+ cacheName + "'" );
			}
			rawCache = createCache ( cacheName );
			this.cacheManager.addCache ( rawCache );
		}
		
		// Decorate cache if necessary.
		Ehcache decoratedCache = decorateCache ( rawCache );
		if ( decoratedCache != rawCache ) {
			this.cacheManager.replaceCacheWithDecoratedCache ( rawCache ,
					decoratedCache );
		}
		this.cache = decoratedCache;
		return this.cache;
	}
	
	/**
	 * Create a raw Cache object based on the configuration of this FactoryBean.
	 */
	private Cache createCache ( String cacheName ) {
		
		Cache cc = new Cache ( cacheName , this.maxElementsInMemory ,
				this.memoryStoreEvictionPolicy , this.overflowToDisk , null ,
				this.eternal , this.timeToLive , this.timeToIdle ,
				this.diskPersistent , this.diskExpiryThreadIntervalSeconds ,
				null , null , this.maxElementsOnDisk );
		if ( diskStorePath != null ) {
			cc.setDiskStorePath ( diskStorePath ) ;
		}
		return cc ;
	}
	
	/**
	 * Decorate the given Cache, if necessary.
	 * 
	 * @param cache
	 *            the raw Cache object, based on the configuration of this
	 *            FactoryBean
	 * @return the (potentially decorated) cache object to be registered with
	 *         the CacheManager
	 */
	protected Ehcache decorateCache ( Ehcache cache ) {
		if ( this.cacheEntryFactory != null ) {
			if ( this.cacheEntryFactory instanceof UpdatingCacheEntryFactory ) {
				return new UpdatingSelfPopulatingCache ( cache ,
						( UpdatingCacheEntryFactory ) this.cacheEntryFactory );
			} else {
				return new SelfPopulatingCache ( cache , this.cacheEntryFactory );
			}
		}
		if ( this.blocking ) {
			return new BlockingCache ( cache );
		}
		return cache;
	}
	
	
	public Object getObject ( ) {
		return this.cache;
	}
	
	public Class getObjectType ( ) {
		return ( this.cache != null ? this.cache.getClass ( ) : Ehcache.class );
	}
	
	public boolean isSingleton ( ) {
		return true;
	}

	public void setDiskStorePath ( String diskStorePath ) {
		this.diskStorePath = diskStorePath;
	}

	public CacheManager getCacheManager ( ) {
		return cacheManager;
	}
	
}